package com.sc.core.util.DataAutoSearch;

import lombok.Data;
import lombok.Getter;
import lombok.experimental.Accessors;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Data
public class SearchDTO {

    private Integer page = 1;
    private Integer pageSize = 10;
    private List<SearchOrder> searchOrders = new ArrayList<>();

    private List<SearchField> searchFields = new ArrayList<>();

    public static SearchDTO build(Map<String, Object> map){
        SearchDTO searchDTO = new SearchDTO();

        if (map.containsKey("page")) {
            searchDTO.setPage(Integer.parseInt(map.get("page").toString()));
        }
        if (map.containsKey("pageSize")) {
            searchDTO.setPageSize(Integer.parseInt(map.get("pageSize").toString()));
        }

        whereParsing(map, searchDTO);
        orderParsing(map, searchDTO);

        return searchDTO;
    }

    private static void orderParsing(Map<String, Object> map, SearchDTO searchDTO) {

        map.forEach((k, v) -> {
            Matcher fieldMatcher = Pattern.compile("^so\\[(\\d+)]\\[f]$").matcher(k);
            if (fieldMatcher.find()){
                int index = Integer.parseInt(fieldMatcher.group(1));
                if (searchDTO.getSearchOrders().isEmpty() || searchDTO.getSearchOrders().size() < index + 1 || searchDTO.getSearchOrders().get(index) == null){
                    searchDTO.getSearchOrders().add(index, new SearchOrder());
                }
                SearchOrder searchOrder = searchDTO.getSearchOrders().get(index);
                searchOrder.setField(v.toString());
                searchOrder.setOrder(SearchOrder.SearchOrderType.DESC);
            }
        });


        map.forEach((k, v) -> {
            Matcher orderMatcher = Pattern.compile("^so\\[(\\d+)]\\[o]$").matcher(k);
            if (orderMatcher.find()){
                int group = Integer.parseInt(orderMatcher.group(1));
                if (searchDTO.getSearchOrders().size() < group - 1) {
                    return;
                }
                String order = v.toString().equalsIgnoreCase("ASC") ? "ASC" : "DESC";
                searchDTO.getSearchOrders().get(group).setOrder(SearchOrder.SearchOrderType.valueOf(order));
            }
        });
    }

    @SuppressWarnings("unchecked")
    private static void whereParsing(Map<String, Object> map, SearchDTO searchDTO) {
        Pattern valuePattern = Pattern.compile("^sf\\[(\\d+)]\\[v](\\[\\d+])?$");
        Pattern typePattern  = Pattern.compile("^sf\\[(\\d+)]\\[t]$");
        Pattern fieldPattern = Pattern.compile("^sf\\[(\\d+)]\\[f]$");
        map.forEach((k, v) -> {
            Matcher fieldMatcher  = fieldPattern.matcher(k);
            if (fieldMatcher.find()){
                searchDTO.getSearchFields().add(Integer.parseInt(fieldMatcher.group(1)), new SearchField().setField(v.toString()));
            }
        });
        map.forEach((k, v) -> {
            Matcher valueMatcher = valuePattern.matcher(k);
            SearchField searchField;
            if (valueMatcher.find()){
                int index = Integer.parseInt(valueMatcher.group(1));
                searchField = searchDTO.getSearchFields().get(index);
                String isArray = valueMatcher.group(2);
                if (isArray == null) {
                    searchField.setValue(v);
                    return;
                }
                if (searchField.getValue() == null) {
                    searchField.setValue(new ArrayList<>());
                }
                ((List<Object>)searchField.getValue()).add(v);
                return;
            }

            Matcher typeMatcher  = typePattern.matcher(k);
            if (typeMatcher.find()){
                int index = Integer.parseInt(typeMatcher.group(1));
                searchField = searchDTO.getSearchFields().get(index);
                searchField.setType(SearchType.getByValue(v.toString()));
            }
        });
    }


    @Data
    @Accessors(chain = true)
    public static class SearchField {
        private String field;
        private Object value;
        private SearchType type;
    }

    @Getter
    public enum SearchType {
        EQUAL("="),
        LIKE("LIKE"),
        GREATER_THAN(">"),
        LESS_THAN("<"),
        GREATER_THAN_EQUAL(">="),
        LESS_THAN_EQUAL("<="),
        NOT_EQUAL("!="),
        IN("IN"),
        NOT_IN("NOT IN");

        private final String value;
        SearchType(String value){
            this.value = value;
        }

        public static SearchType getByValue(String value){
            for (SearchType searchType : values()) {
                if (searchType.value.equalsIgnoreCase(value)){
                    return searchType;
                }
            }
            return null;
        }
    }

    @Data
    public static class SearchOrder {
        private String field;
        private SearchOrderType order;

        public enum SearchOrderType{
            ASC,
            DESC;
        }
    }
}
